using Microsoft.Extensions.Logging;
using System.Data.Common;

namespace SeataNet.Core.AT.UndoLog
{
    /// <summary>
    /// UndoLog表管理器接口
    ///
    /// 定义undo_log表的DDL操作标准接口（创建、检查、删除）。
    ///
    /// 职责：
    /// 1. 自动创建undo_log表结构（如果不存在）
    /// 2. 检查undo_log表是否已存在
    /// 3. 删除undo_log表（用于清理或重置）
    ///
    /// 工作原理：
    /// - 应用启动时调用CreateTableIfNotExistsAsync确保表存在
    /// - 使用数据库特定的DDL语法创建表
    /// - 为不同数据库提供不同的实现类
    ///
    /// undo_log表结构：
    /// ```sql
    /// CREATE TABLE undo_log (
    ///   branch_id BIGINT NOT NULL,           -- 分支事务ID
    ///   xid VARCHAR(128) NOT NULL,           -- 全局事务ID
    ///   context VARCHAR(128),                -- 事务上下文
    ///   rollback_info TEXT/LONGTEXT NOT NULL,-- 回滚信息（JSON格式）
    ///   log_status INT NOT NULL,             -- 日志状态（0=正常，1=防御性）
    ///   log_created DATETIME NOT NULL,       -- 创建时间
    ///   log_modified DATETIME NOT NULL,      -- 修改时间
    ///   PRIMARY KEY (branch_id, xid)         -- 复合主键
    /// );
    ///
    /// -- 索引
    /// CREATE INDEX idx_xid ON undo_log(xid);                    -- 按XID查询
    /// CREATE INDEX idx_log_created ON undo_log(log_created);   -- 清理过期日志
    /// ```
    ///
    /// 实现类：
    /// - SqlServerUndoLogTableManager：SQL Server数据库（使用NVARCHAR(MAX)）
    /// - MySqlUndoLogTableManager：MySQL数据库（使用LONGTEXT和InnoDB引擎）
    /// - PostgreSqlUndoLogTableManager：PostgreSQL数据库（使用TEXT类型）
    ///
    /// 使用场景：
    /// - 应用启动：自动创建undo_log表
    /// - 数据库迁移：检查表是否需要创建
    /// - 测试环境：清理和重建undo_log表
    ///
    /// 注意事项：
    /// - 创建表操作需要数据库DDL权限
    /// - 不同数据库的数据类型和索引语法不同
    /// - 生产环境建议使用数据库迁移工具（如FluentMigrator、EF Migrations）
    /// - 表名固定为"undo_log"，不可配置
    /// </summary>
    public interface IUndoLogTableManager
    {
        /// <summary>
        /// 创建undo_log表（如果不存在）
        ///
        /// 应用启动时调用，自动创建undo_log表和索引。
        ///
        /// 执行流程：
        /// 1. 检查undo_log表是否已存在（调用TableExistsAsync）
        /// 2. 如果表已存在，记录日志并返回
        /// 3. 如果表不存在，执行CREATE TABLE语句
        /// 4. 创建索引（xid索引和log_created索引）
        /// 5. 记录创建成功日志
        ///
        /// 表结构特点：
        /// - 复合主键：(branch_id, xid)，确保每个分支的undo_log唯一
        /// - rollback_info字段：存储JSON格式的UndoLogRecord
        /// - xid索引：加速按全局事务查询
        /// - log_created索引：加速清理过期日志
        ///
        /// 调用时机：
        /// - 应用启动时（在应用启动类中调用）
        /// - 数据库初始化时
        /// - 集成测试前
        ///
        /// 注意事项：
        /// - 需要数据库CREATE TABLE权限
        /// - 如果并发调用，可能导致重复创建异常（需要处理）
        /// - 生产环境建议使用数据库迁移工具管理表结构
        /// - 不同数据库的CREATE TABLE语法可能不同
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开</param>
        /// <exception cref="DbException">创建表失败时抛出（如权限不足）</exception>
        Task CreateTableIfNotExistsAsync(DbConnection connection);

        /// <summary>
        /// 检查undo_log表是否存在
        ///
        /// 查询INFORMATION_SCHEMA系统表，判断undo_log表是否已创建。
        ///
        /// 执行流程：
        /// 1. 查询INFORMATION_SCHEMA.TABLES系统表
        /// 2. 使用参数化查询，传入表名参数
        /// 3. 返回查询结果（存在返回true，不存在返回false）
        ///
        /// 实现细节：
        /// - SQL Server：查询INFORMATION_SCHEMA.TABLES
        /// - MySQL：查询INFORMATION_SCHEMA.TABLES并过滤TABLE_SCHEMA = DATABASE()
        /// - PostgreSQL：查询information_schema.tables并过滤table_schema = 'public'
        ///
        /// 使用场景：
        /// - CreateTableIfNotExistsAsync中检查表是否存在
        /// - 应用健康检查
        /// - 诊断工具
        ///
        /// 注意事项：
        /// - 需要数据库SELECT权限
        /// - 返回值是布尔值，不抛出异常
        /// - 不同数据库的INFORMATION_SCHEMA结构略有不同
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开</param>
        /// <returns>表存在返回true，不存在返回false</returns>
        Task<bool> TableExistsAsync(DbConnection connection);

        /// <summary>
        /// 删除undo_log表
        ///
        /// 删除整个undo_log表及其所有数据和索引。
        ///
        /// 执行流程：
        /// 1. 记录警告日志（删除表是危险操作）
        /// 2. 执行DROP TABLE IF EXISTS语句
        /// 3. 记录删除成功日志
        ///
        /// 使用场景：
        /// - 测试环境：清理测试数据
        /// - 集成测试：每次测试前重建表
        /// - 数据库迁移：重置表结构
        ///
        /// 注意事项：
        /// - ⚠️ 危险操作：删除表会丢失所有undo_log数据
        /// - 需要数据库DROP TABLE权限
        /// - 生产环境禁止使用此方法
        /// - 如果表不存在，不会报错（使用IF EXISTS）
        /// - 删除表会影响正在进行的分布式事务回滚
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开</param>
        /// <exception cref="DbException">删除表失败时抛出（如权限不足或表被锁定）</exception>
        Task DropTableAsync(DbConnection connection);
    }

    /// <summary>
    /// SQL Server undo_log表管理器
    ///
    /// 职责：
    /// 1. 为SQL Server数据库创建undo_log表
    /// 2. 使用SQL Server特定的数据类型和语法
    /// 3. 创建复合主键和索引
    ///
    /// SQL Server特定语法：
    /// - NVARCHAR(MAX)：存储JSON格式的rollback_info（支持Unicode）
    /// - DATETIME：时间类型（精度到毫秒）
    /// - CONSTRAINT pk_undo_log PRIMARY KEY：命名主键约束
    /// - CREATE INDEX：创建独立索引
    ///
    /// 表结构（SQL Server）：
    /// ```sql
    /// CREATE TABLE undo_log (
    ///     branch_id BIGINT NOT NULL,
    ///     xid VARCHAR(128) NOT NULL,
    ///     context VARCHAR(128),
    ///     rollback_info NVARCHAR(MAX) NOT NULL,  -- 使用NVARCHAR(MAX)支持大JSON
    ///     log_status INT NOT NULL,
    ///     log_created DATETIME NOT NULL,
    ///     log_modified DATETIME NOT NULL,
    ///     CONSTRAINT pk_undo_log PRIMARY KEY (branch_id, xid)
    /// );
    ///
    /// CREATE INDEX idx_undo_log_xid ON undo_log(xid);
    /// CREATE INDEX idx_undo_log_log_created ON undo_log(log_created);
    /// ```
    ///
    /// 数据类型选择：
    /// - NVARCHAR(MAX)：支持Unicode，最大2GB，适合存储JSON
    /// - VARCHAR(128)：存储XID和context，节省空间
    /// - BIGINT：存储branch_id，支持大数值
    /// - DATETIME：精度到3.33毫秒，适合日志时间
    ///
    /// 索引策略：
    /// - 主键索引：(branch_id, xid) 自动创建聚集索引
    /// - idx_undo_log_xid：加速按XID查询所有分支
    /// - idx_undo_log_log_created：加速清理过期日志
    ///
    /// 使用场景：
    /// - SQL Server 2012及以上版本
    /// - Azure SQL Database
    /// - SQL Server Express
    ///
    /// 注意事项：
    /// - 需要SQL Server CREATE TABLE权限
    /// - NVARCHAR(MAX)比VARCHAR(MAX)占用更多空间（约2倍）
    /// - 如果数据库使用非Unicode排序规则，建议使用VARCHAR(MAX)
    /// - 索引名称包含表名前缀以避免冲突
    /// </summary>
    public class SqlServerUndoLogTableManager : IUndoLogTableManager
    {
        private readonly ILogger<SqlServerUndoLogTableManager> _logger;
        private const string TABLE_NAME = "undo_log";

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="logger">日志记录器</param>
        public SqlServerUndoLogTableManager(ILogger<SqlServerUndoLogTableManager> logger)
        {
            _logger = logger;
        }

        /// <summary>
        /// 创建undo_log表（如果不存在）- SQL Server实现
        ///
        /// 执行流程：
        /// 1. 调用TableExistsAsync检查表是否已存在
        /// 2. 如果表已存在，记录信息日志并返回
        /// 3. 如果表不存在：
        ///    - 记录开始创建的信息日志
        ///    - 生成CREATE TABLE DDL语句
        ///    - 在同一批次中创建2个索引（xid索引和log_created索引）
        ///    - 执行DDL语句
        ///    - 记录创建成功日志
        ///
        /// SQL Server DDL特点：
        /// - 复合主键：CONSTRAINT pk_undo_log PRIMARY KEY (branch_id, xid)
        /// - 索引语法：CREATE INDEX idx_table_column ON table(column)
        /// - 可以在一个批次中执行多条DDL语句
        /// - 不支持IF NOT EXISTS语法（需要先检查表是否存在）
        ///
        /// 数据类型说明：
        /// - NVARCHAR(MAX)：rollback_info字段，最大2GB，支持Unicode
        /// - VARCHAR(128)：xid和context字段，固定长度，节省空间
        /// - BIGINT：branch_id字段，8字节整数
        /// - INT：log_status字段，4字节整数
        /// - DATETIME：log_created和log_modified字段，精度3.33毫秒
        ///
        /// 注意事项：
        /// - 先检查表存在性可以避免重复创建异常
        /// - 并发创建可能导致竞态条件，建议使用数据库锁
        /// - 索引在表创建后立即创建，可能需要一定时间
        /// - 如果索引创建失败，表创建成功但没有索引
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开</param>
        /// <exception cref="DbException">创建表或索引失败时抛出</exception>
        public async Task CreateTableIfNotExistsAsync(DbConnection connection)
        {
            if (await TableExistsAsync(connection))
            {
                _logger.LogInformation($"表 {TABLE_NAME} 已存在，跳过创建");
                return;
            }

            _logger.LogInformation($"开始创建表 {TABLE_NAME}");

            var createTableSql = $@"
                CREATE TABLE {TABLE_NAME} (
                    branch_id BIGINT NOT NULL,
                    xid VARCHAR(128) NOT NULL,
                    context VARCHAR(128),
                    rollback_info NVARCHAR(MAX) NOT NULL,
                    log_status INT NOT NULL,
                    log_created DATETIME NOT NULL,
                    log_modified DATETIME NOT NULL,
                    CONSTRAINT pk_{TABLE_NAME} PRIMARY KEY (branch_id, xid)
                );

                CREATE INDEX idx_{TABLE_NAME}_xid ON {TABLE_NAME}(xid);
                CREATE INDEX idx_{TABLE_NAME}_log_created ON {TABLE_NAME}(log_created);
            ";

            using var command = connection.CreateCommand();
            command.CommandText = createTableSql;

            await command.ExecuteNonQueryAsync();

            _logger.LogInformation($"表 {TABLE_NAME} 创建成功");
        }

        /// <summary>
        /// 检查undo_log表是否存在 - SQL Server实现
        ///
        /// 执行流程：
        /// 1. 构造查询INFORMATION_SCHEMA.TABLES的SQL语句
        /// 2. 使用参数化查询，传入表名参数
        /// 3. 执行查询并获取结果（COUNT(1)）
        /// 4. 如果结果大于0，返回true（表存在）
        /// 5. 否则返回false（表不存在）
        ///
        /// SQL Server INFORMATION_SCHEMA特点：
        /// - INFORMATION_SCHEMA.TABLES是系统视图
        /// - TABLE_NAME列存储表名（不区分大小写）
        /// - 查询当前数据库的所有表
        /// - 不需要指定TABLE_SCHEMA（自动使用当前数据库）
        ///
        /// 参数化查询：
        /// - 使用@TableName参数避免SQL注入
        /// - 参数名前缀为@（SQL Server标准）
        /// - 参数值为TABLE_NAME常量（"undo_log"）
        ///
        /// 返回值：
        /// - true：表存在
        /// - false：表不存在
        ///
        /// 注意事项：
        /// - 需要SELECT权限访问INFORMATION_SCHEMA
        /// - TABLE_NAME查询不区分大小写
        /// - 查询结果是整数，需要转换为布尔值
        /// - 不会抛出异常，即使表不存在也返回false
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开</param>
        /// <returns>表存在返回true，不存在返回false</returns>
        public async Task<bool> TableExistsAsync(DbConnection connection)
        {
            var checkSql = @"
                SELECT COUNT(1)
                FROM INFORMATION_SCHEMA.TABLES
                WHERE TABLE_NAME = @TableName
            ";

            using var command = connection.CreateCommand();
            command.CommandText = checkSql;

            var parameter = command.CreateParameter();
            parameter.ParameterName = "@TableName";
            parameter.Value = TABLE_NAME;
            command.Parameters.Add(parameter);

            var result = await command.ExecuteScalarAsync();
            return Convert.ToInt32(result) > 0;
        }

        /// <summary>
        /// 删除undo_log表 - SQL Server实现
        ///
        /// 执行流程：
        /// 1. 记录警告日志（删除表是危险操作）
        /// 2. 生成DROP TABLE IF EXISTS语句
        /// 3. 执行DROP TABLE语句
        /// 4. 记录删除成功日志
        ///
        /// SQL Server DDL特点：
        /// - DROP TABLE IF EXISTS语法（SQL Server 2016+）
        /// - 如果表不存在，不会报错
        /// - 删除表会自动删除所有索引和约束
        /// - 删除是原子操作，要么全部成功要么全部失败
        ///
        /// 注意事项：
        /// - ⚠️ 危险操作：删除表会丢失所有数据
        /// - 需要DROP TABLE权限
        /// - 如果表被锁定（有活跃事务），删除会失败
        /// - 删除后需要重新创建表才能使用AT模式
        /// - 生产环境禁止使用
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开</param>
        /// <exception cref="DbException">删除表失败时抛出（如权限不足或表被锁定）</exception>
        public async Task DropTableAsync(DbConnection connection)
        {
            _logger.LogWarning($"开始删除表 {TABLE_NAME}");

            var dropSql = $"DROP TABLE IF EXISTS {TABLE_NAME}";

            using var command = connection.CreateCommand();
            command.CommandText = dropSql;

            await command.ExecuteNonQueryAsync();

            _logger.LogWarning($"表 {TABLE_NAME} 删除成功");
        }
    }

    /// <summary>
    /// MySQL undo_log表管理器
    ///
    /// 职责：
    /// 1. 为MySQL数据库创建undo_log表
    /// 2. 使用MySQL特定的数据类型和语法
    /// 3. 配置InnoDB存储引擎和UTF8MB4字符集
    ///
    /// MySQL特定语法：
    /// - LONGTEXT：存储大文本（最大4GB），适合存储JSON
    /// - CREATE TABLE IF NOT EXISTS：MySQL原生支持，无需先检查
    /// - KEY idx_name：MySQL索引语法（等同于CREATE INDEX）
    /// - ENGINE=InnoDB：指定存储引擎（支持事务）
    /// - DEFAULT CHARSET=utf8mb4：支持完整Unicode（包括Emoji）
    ///
    /// 表结构（MySQL）：
    /// ```sql
    /// CREATE TABLE IF NOT EXISTS undo_log (
    ///     branch_id BIGINT NOT NULL,
    ///     xid VARCHAR(128) NOT NULL,
    ///     context VARCHAR(128),
    ///     rollback_info LONGTEXT NOT NULL,  -- 使用LONGTEXT存储大JSON
    ///     log_status INT NOT NULL,
    ///     log_created DATETIME NOT NULL,
    ///     log_modified DATETIME NOT NULL,
    ///     PRIMARY KEY (branch_id, xid),
    ///     KEY idx_xid (xid),
    ///     KEY idx_log_created (log_created)
    /// ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
    /// ```
    ///
    /// 数据类型选择：
    /// - LONGTEXT：最大4GB，适合存储大JSON，自动处理溢出
    /// - VARCHAR(128)：存储XID和context，变长存储，节省空间
    /// - BIGINT：存储branch_id，8字节整数
    /// - INT：存储log_status，4字节整数
    /// - DATETIME：精度到秒（MySQL 5.6+支持微秒）
    ///
    /// 存储引擎特点：
    /// - InnoDB：支持事务、行级锁、外键约束
    /// - 聚集索引：主键索引（branch_id, xid）
    /// - 二级索引：idx_xid和idx_log_created
    ///
    /// 字符集选择：
    /// - utf8mb4：支持完整Unicode（包括4字节字符如Emoji）
    /// - 比utf8支持更多字符（utf8只支持3字节字符）
    ///
    /// 使用场景：
    /// - MySQL 5.6及以上版本
    /// - MariaDB 10.0及以上版本
    /// - TiDB数据库（兼容MySQL协议）
    ///
    /// 注意事项：
    /// - 需要MySQL CREATE TABLE权限
    /// - LONGTEXT比NVARCHAR(MAX)（SQL Server）更节省空间
    /// - IF NOT EXISTS避免重复创建异常
    /// - InnoDB引擎是MySQL默认引擎（5.5+）
    /// - utf8mb4是推荐的字符集（避免Emoji存储问题）
    /// </summary>
    public class MySqlUndoLogTableManager : IUndoLogTableManager
    {
        private readonly ILogger<MySqlUndoLogTableManager> _logger;
        private const string TABLE_NAME = "undo_log";

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="logger">日志记录器</param>
        public MySqlUndoLogTableManager(ILogger<MySqlUndoLogTableManager> logger)
        {
            _logger = logger;
        }

        /// <summary>
        /// 创建undo_log表（如果不存在）- MySQL实现
        ///
        /// 执行流程：
        /// 1. 记录开始创建的信息日志
        /// 2. 生成CREATE TABLE IF NOT EXISTS DDL语句
        /// 3. 在CREATE TABLE语句中同时定义：
        ///    - 表字段定义
        ///    - 主键定义：PRIMARY KEY (branch_id, xid)
        ///    - 两个索引定义：KEY idx_xid, KEY idx_log_created
        ///    - 存储引擎：ENGINE=InnoDB
        ///    - 字符集：DEFAULT CHARSET=utf8mb4
        /// 4. 执行DDL语句
        /// 5. 记录创建完成日志
        ///
        /// MySQL DDL特点：
        /// - IF NOT EXISTS：原生支持，表存在时不报错
        /// - 表定义、索引、引擎、字符集在一条SQL中完成
        /// - KEY语法：在表定义中直接创建索引
        /// - PRIMARY KEY：复合主键，自动创建聚集索引
        ///
        /// 与SQL Server的区别：
        /// - 无需先检查表是否存在（IF NOT EXISTS自动处理）
        /// - 索引在CREATE TABLE语句中定义（而非单独的CREATE INDEX）
        /// - 需要指定存储引擎（InnoDB）
        /// - 需要指定字符集（utf8mb4）
        ///
        /// 存储引擎说明：
        /// - InnoDB：支持事务、ACID特性、行级锁
        /// - 如果不指定ENGINE，使用MySQL默认引擎
        ///
        /// 字符集说明：
        /// - utf8mb4：支持完整Unicode，包括4字节字符（Emoji等）
        /// - 如果不指定CHARSET，使用数据库默认字符集
        ///
        /// 注意事项：
        /// - IF NOT EXISTS使得方法可以安全地重复调用
        /// - 表结构在一条SQL中定义完成，原子性更好
        /// - MySQL 5.6+支持IF NOT EXISTS语法
        /// - 索引在表创建时同步创建，避免了二次DDL
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开</param>
        /// <exception cref="DbException">创建表或索引失败时抛出</exception>
        public async Task CreateTableIfNotExistsAsync(DbConnection connection)
        {
            _logger.LogInformation($"开始创建表 {TABLE_NAME}");

            var createTableSql = $@"
                CREATE TABLE IF NOT EXISTS {TABLE_NAME} (
                    branch_id BIGINT NOT NULL,
                    xid VARCHAR(128) NOT NULL,
                    context VARCHAR(128),
                    rollback_info LONGTEXT NOT NULL,
                    log_status INT NOT NULL,
                    log_created DATETIME NOT NULL,
                    log_modified DATETIME NOT NULL,
                    PRIMARY KEY (branch_id, xid),
                    KEY idx_xid (xid),
                    KEY idx_log_created (log_created)
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
            ";

            using var command = connection.CreateCommand();
            command.CommandText = createTableSql;

            await command.ExecuteNonQueryAsync();

            _logger.LogInformation($"表 {TABLE_NAME} 创建完成");
        }

        /// <summary>
        /// 检查undo_log表是否存在 - MySQL实现
        ///
        /// 执行流程：
        /// 1. 构造查询INFORMATION_SCHEMA.TABLES的SQL语句
        /// 2. 使用参数化查询，传入表名参数
        /// 3. 添加TABLE_SCHEMA过滤条件：TABLE_SCHEMA = DATABASE()
        ///    - DATABASE()函数返回当前数据库名
        ///    - 确保只查询当前数据库的表
        /// 4. 执行查询并获取结果（COUNT(1)）
        /// 5. 如果结果大于0，返回true（表存在）
        /// 6. 否则返回false（表不存在）
        ///
        /// MySQL INFORMATION_SCHEMA特点：
        /// - INFORMATION_SCHEMA.TABLES是系统数据库
        /// - TABLE_NAME列存储表名（区分大小写，取决于操作系统）
        /// - TABLE_SCHEMA列存储数据库名
        /// - 查询所有数据库的表，需要过滤TABLE_SCHEMA
        ///
        /// DATABASE()函数：
        /// - 返回当前连接使用的数据库名
        /// - 等同于SELECT DATABASE()
        /// - 确保只查询当前数据库的表
        ///
        /// 与SQL Server的区别：
        /// - 需要过滤TABLE_SCHEMA（SQL Server自动使用当前数据库）
        /// - 使用DATABASE()函数获取当前数据库名
        /// - TABLE_NAME可能区分大小写（Linux下区分，Windows下不区分）
        ///
        /// 注意事项：
        /// - 需要SELECT权限访问INFORMATION_SCHEMA
        /// - TABLE_NAME大小写敏感性取决于lower_case_table_names设置
        /// - 查询结果是整数，需要转换为布尔值
        /// - 不会抛出异常，即使表不存在也返回false
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开</param>
        /// <returns>表存在返回true，不存在返回false</returns>
        public async Task<bool> TableExistsAsync(DbConnection connection)
        {
            var checkSql = $@"
                SELECT COUNT(1)
                FROM INFORMATION_SCHEMA.TABLES
                WHERE TABLE_SCHEMA = DATABASE()
                AND TABLE_NAME = @TableName
            ";

            using var command = connection.CreateCommand();
            command.CommandText = checkSql;

            var parameter = command.CreateParameter();
            parameter.ParameterName = "@TableName";
            parameter.Value = TABLE_NAME;
            command.Parameters.Add(parameter);

            var result = await command.ExecuteScalarAsync();
            return Convert.ToInt32(result) > 0;
        }

        /// <summary>
        /// 删除undo_log表 - MySQL实现
        ///
        /// 执行流程：
        /// 1. 记录警告日志（删除表是危险操作）
        /// 2. 生成DROP TABLE IF EXISTS语句
        /// 3. 执行DROP TABLE语句
        /// 4. 记录删除成功日志
        ///
        /// MySQL DDL特点：
        /// - DROP TABLE IF EXISTS语法（MySQL原生支持）
        /// - 如果表不存在，不会报错
        /// - 删除表会自动删除所有索引
        /// - InnoDB引擎会释放表空间
        ///
        /// 注意事项：
        /// - ⚠️ 危险操作：删除表会丢失所有数据
        /// - 需要DROP TABLE权限
        /// - 如果表被锁定（有活跃事务），删除会等待或失败
        /// - 删除后需要重新创建表才能使用AT模式
        /// - 生产环境禁止使用
        /// - InnoDB引擎删除表会释放.ibd文件
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开</param>
        /// <exception cref="DbException">删除表失败时抛出（如权限不足或表被锁定）</exception>
        public async Task DropTableAsync(DbConnection connection)
        {
            _logger.LogWarning($"开始删除表 {TABLE_NAME}");

            var dropSql = $"DROP TABLE IF EXISTS {TABLE_NAME}";

            using var command = connection.CreateCommand();
            command.CommandText = dropSql;

            await command.ExecuteNonQueryAsync();

            _logger.LogWarning($"表 {TABLE_NAME} 删除成功");
        }
    }

    /// <summary>
    /// PostgreSQL undo_log表管理器
    ///
    /// 职责：
    /// 1. 为PostgreSQL数据库创建undo_log表
    /// 2. 使用PostgreSQL特定的数据类型和语法
    /// 3. 创建索引时使用IF NOT EXISTS语法
    ///
    /// PostgreSQL特定语法：
    /// - TEXT：存储大文本（无长度限制），适合存储JSON
    /// - TIMESTAMP：时间类型（精度到微秒，带时区）
    /// - CREATE TABLE IF NOT EXISTS：PostgreSQL原生支持
    /// - CREATE INDEX IF NOT EXISTS：避免索引重复创建
    ///
    /// 表结构（PostgreSQL）：
    /// ```sql
    /// CREATE TABLE IF NOT EXISTS undo_log (
    ///     branch_id BIGINT NOT NULL,
    ///     xid VARCHAR(128) NOT NULL,
    ///     context VARCHAR(128),
    ///     rollback_info TEXT NOT NULL,  -- 使用TEXT存储JSON
    ///     log_status INT NOT NULL,
    ///     log_created TIMESTAMP NOT NULL,
    ///     log_modified TIMESTAMP NOT NULL,
    ///     PRIMARY KEY (branch_id, xid)
    /// );
    ///
    /// CREATE INDEX IF NOT EXISTS idx_undo_log_xid ON undo_log(xid);
    /// CREATE INDEX IF NOT EXISTS idx_undo_log_log_created ON undo_log(log_created);
    /// ```
    ///
    /// 数据类型选择：
    /// - TEXT：无长度限制，适合存储大JSON，性能与VARCHAR相同
    /// - VARCHAR(128)：存储XID和context，变长存储
    /// - BIGINT：存储branch_id，8字节整数
    /// - INT：存储log_status，4字节整数（在PostgreSQL中称为INTEGER）
    /// - TIMESTAMP：精度到微秒，支持时区（TIMESTAMPTZ）
    ///
    /// 索引特点：
    /// - 主键索引：自动创建B-Tree索引
    /// - 二级索引：单独使用CREATE INDEX语句创建
    /// - IF NOT EXISTS：避免重复创建索引时报错
    ///
    /// 与其他数据库的区别：
    /// - TEXT vs LONGTEXT(MySQL) / NVARCHAR(MAX)(SQL Server)：TEXT更简洁
    /// - TIMESTAMP vs DATETIME：TIMESTAMP精度更高（微秒级）
    /// - 索引需要单独创建（不能在表定义中使用KEY语法）
    /// - 支持JSON/JSONB数据类型（更适合存储JSON）
    ///
    /// 使用场景：
    /// - PostgreSQL 9.5及以上版本
    /// - TimescaleDB（基于PostgreSQL的时序数据库）
    /// - CockroachDB（兼容PostgreSQL协议）
    ///
    /// 注意事项：
    /// - 需要PostgreSQL CREATE TABLE权限
    /// - TEXT类型无长度限制，性能与VARCHAR相同
    /// - TIMESTAMP不带时区，建议使用TIMESTAMPTZ
    /// - 索引名称需要全局唯一（不能与其他表的索引重名）
    /// - IF NOT EXISTS确保幂等性（可重复执行）
    /// </summary>
    public class PostgreSqlUndoLogTableManager : IUndoLogTableManager
    {
        private readonly ILogger<PostgreSqlUndoLogTableManager> _logger;
        private const string TABLE_NAME = "undo_log";

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="logger">日志记录器</param>
        public PostgreSqlUndoLogTableManager(ILogger<PostgreSqlUndoLogTableManager> logger)
        {
            _logger = logger;
        }

        /// <summary>
        /// 创建undo_log表（如果不存在）- PostgreSQL实现
        ///
        /// 执行流程：
        /// 1. 记录开始创建的信息日志
        /// 2. 生成CREATE TABLE IF NOT EXISTS DDL语句
        /// 3. 在CREATE TABLE语句中定义：
        ///    - 表字段定义
        ///    - 主键定义：PRIMARY KEY (branch_id, xid)
        /// 4. 单独创建2个索引（使用IF NOT EXISTS语法）：
        ///    - CREATE INDEX IF NOT EXISTS idx_undo_log_xid
        ///    - CREATE INDEX IF NOT EXISTS idx_undo_log_log_created
        /// 5. 执行DDL语句（包含表创建和索引创建）
        /// 6. 记录创建完成日志
        ///
        /// PostgreSQL DDL特点：
        /// - IF NOT EXISTS：表和索引都支持IF NOT EXISTS
        /// - 主键定义：PRIMARY KEY在表定义中声明
        /// - 索引定义：需要使用单独的CREATE INDEX语句
        /// - 多条DDL语句可以在一个批次中执行（用分号分隔）
        ///
        /// 与其他数据库的区别：
        /// - 索引需要单独创建（不能使用KEY语法）
        /// - 索引名称必须全局唯一（建议加表名前缀）
        /// - IF NOT EXISTS确保幂等性
        /// - 不需要指定存储引擎（PostgreSQL没有存储引擎概念）
        ///
        /// TEXT类型说明：
        /// - 无长度限制，性能与VARCHAR相同
        /// - 支持完整Unicode（UTF-8编码）
        /// - 适合存储JSON字符串
        ///
        /// TIMESTAMP类型说明：
        /// - 精度到微秒（6位小数）
        /// - 范围：4713 BC 到 294276 AD
        /// - 不带时区（使用TIMESTAMPTZ可以带时区）
        ///
        /// 注意事项：
        /// - IF NOT EXISTS确保可以安全地重复调用
        /// - 索引名称包含表名前缀以避免冲突
        /// - PostgreSQL区分大小写（标识符用双引号）
        /// - 默认情况下标识符会转换为小写
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开</param>
        /// <exception cref="DbException">创建表或索引失败时抛出</exception>
        public async Task CreateTableIfNotExistsAsync(DbConnection connection)
        {
            _logger.LogInformation($"开始创建表 {TABLE_NAME}");

            var createTableSql = $@"
                CREATE TABLE IF NOT EXISTS {TABLE_NAME} (
                    branch_id BIGINT NOT NULL,
                    xid VARCHAR(128) NOT NULL,
                    context VARCHAR(128),
                    rollback_info TEXT NOT NULL,
                    log_status INT NOT NULL,
                    log_created TIMESTAMP NOT NULL,
                    log_modified TIMESTAMP NOT NULL,
                    PRIMARY KEY (branch_id, xid)
                );

                CREATE INDEX IF NOT EXISTS idx_{TABLE_NAME}_xid ON {TABLE_NAME}(xid);
                CREATE INDEX IF NOT EXISTS idx_{TABLE_NAME}_log_created ON {TABLE_NAME}(log_created);
            ";

            using var command = connection.CreateCommand();
            command.CommandText = createTableSql;

            await command.ExecuteNonQueryAsync();

            _logger.LogInformation($"表 {TABLE_NAME} 创建完成");
        }

        /// <summary>
        /// 检查undo_log表是否存在 - PostgreSQL实现
        ///
        /// 执行流程：
        /// 1. 构造查询information_schema.tables的SQL语句
        /// 2. 使用参数化查询，传入表名参数
        /// 3. 添加table_schema过滤条件：table_schema = 'public'
        ///    - PostgreSQL默认schema是public
        ///    - 如果表在其他schema中，需要修改查询条件
        /// 4. 执行查询并获取结果（COUNT(1)）
        /// 5. 如果结果大于0，返回true（表存在）
        /// 6. 否则返回false（表不存在）
        ///
        /// PostgreSQL INFORMATION_SCHEMA特点：
        /// - information_schema.tables是系统视图
        /// - table_name列存储表名（小写，除非用双引号创建）
        /// - table_schema列存储schema名（默认为public）
        /// - 需要过滤table_schema以确定表所在的schema
        ///
        /// Schema概念：
        /// - PostgreSQL支持多个schema（类似MySQL的数据库）
        /// - 默认schema是public
        /// - 表的完全限定名：schema.table_name
        ///
        /// 与其他数据库的区别：
        /// - 使用table_schema = 'public'（而非DATABASE()函数）
        /// - information_schema全小写（而非INFORMATION_SCHEMA）
        /// - table_name默认小写（除非创建时用双引号）
        ///
        /// 注意事项：
        /// - 需要SELECT权限访问information_schema
        /// - table_name小写匹配（PostgreSQL默认转小写）
        /// - 如果表在非public schema中，需要修改查询
        /// - 查询结果是整数，需要转换为布尔值
        /// - 不会抛出异常，即使表不存在也返回false
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开</param>
        /// <returns>表存在返回true，不存在返回false</returns>
        public async Task<bool> TableExistsAsync(DbConnection connection)
        {
            var checkSql = @"
                SELECT COUNT(1)
                FROM information_schema.tables
                WHERE table_schema = 'public'
                AND table_name = @TableName
            ";

            using var command = connection.CreateCommand();
            command.CommandText = checkSql;

            var parameter = command.CreateParameter();
            parameter.ParameterName = "@TableName";
            parameter.Value = TABLE_NAME;
            command.Parameters.Add(parameter);

            var result = await command.ExecuteScalarAsync();
            return Convert.ToInt32(result) > 0;
        }

        /// <summary>
        /// 删除undo_log表 - PostgreSQL实现
        ///
        /// 执行流程：
        /// 1. 记录警告日志（删除表是危险操作）
        /// 2. 生成DROP TABLE IF EXISTS语句
        /// 3. 执行DROP TABLE语句
        /// 4. 记录删除成功日志
        ///
        /// PostgreSQL DDL特点：
        /// - DROP TABLE IF EXISTS语法（PostgreSQL 8.2+支持）
        /// - 如果表不存在，不会报错
        /// - 删除表会自动删除所有索引、约束、触发器
        /// - 删除是原子操作，要么全部成功要么全部失败
        ///
        /// CASCADE选项：
        /// - DROP TABLE ... CASCADE：同时删除依赖对象（视图、外键等）
        /// - 当前实现未使用CASCADE，如果有依赖会报错
        ///
        /// 注意事项：
        /// - ⚠️ 危险操作：删除表会丢失所有数据
        /// - 需要DROP TABLE权限
        /// - 如果表被其他会话锁定，删除会等待或失败
        /// - 删除后需要重新创建表才能使用AT模式
        /// - 生产环境禁止使用
        /// - 如果表有依赖对象（如视图），需要使用CASCADE
        /// </summary>
        /// <param name="connection">数据库连接，必须已打开</param>
        /// <exception cref="DbException">删除表失败时抛出（如权限不足或表被锁定）</exception>
        public async Task DropTableAsync(DbConnection connection)
        {
            _logger.LogWarning($"开始删除表 {TABLE_NAME}");

            var dropSql = $"DROP TABLE IF EXISTS {TABLE_NAME}";

            using var command = connection.CreateCommand();
            command.CommandText = dropSql;

            await command.ExecuteNonQueryAsync();

            _logger.LogWarning($"表 {TABLE_NAME} 删除成功");
        }
    }
}
